package org.clockworkmages.games.anno1186.model.character;

import java.beans.Transient;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;

import org.clockworkmages.games.anno1186.model.effect.ConditionStatus;
import org.clockworkmages.games.anno1186.model.item.Item;

public abstract class GameCharacter {

	private String name;

	private List<Item> inventory = new ArrayList<Item>();

	private Map<String, Item> equippedItems = new HashMap<String, Item>();

	private Map<String, Integer> skills = new HashMap<String, Integer>();

	private Set<String> spells = new HashSet<String>();

	private Map<String, ConditionStatus> conditions = new HashMap<String, ConditionStatus>();

	private Set<String> perks = new HashSet<String>();

	private Map<String, Double> baseStats = new HashMap<String, Double>();
	private Map<String, Double> effectiveStats = new HashMap<String, Double>();

	private Map<String, Double> derivates = new HashMap<String, Double>();

	private Map<String, Double> damageMinimals = new HashMap<String, Double>();
	private Map<String, Double> damageRanges = new HashMap<String, Double>();
	private Map<String, Double> damageResistances = new HashMap<String, Double>();

	private int gold;

	private int gender;

	private int level = 1;
	private int experience = 0;

	/**
	 * Informs if the character's name is a personal name or not, and whether it
	 * should be preceded by "the" in a fraze (e.g.
	 * "You hit *the* wolf for 5 damage" but
	 * "You hit Johny Uglynose for 5 damage").
	 */
	private boolean addThe = false;

	/**
	 * Transient flag that informs the game that some of the stats, skills,
	 * perks or conditions have changed and that the derived stats need to be
	 * recalculated.
	 */
	private boolean statsAreStale;

	public List<Item> getInventory() {
		return inventory;
	}

	public void setInventory(List<Item> inventory) {
		this.inventory = inventory;
	}

	public Map<String, Item> getEquippedItems() {
		return equippedItems;
	}

	public int getBaseStatAsInt(String statId) {
		return getStatAsInt(baseStats, statId);
	}

	public int getEffectiveStatAsInt(String statId) {
		return getStatAsInt(effectiveStats, statId);
	}

	public int getDerivateAsInt(String statId) {
		return getStatAsInt(derivates, statId);
	}

	private int getStatAsInt(Map<String, Double> stats, String statId) {
		Double value = stats.get(statId);
		if (value == null) {
			return 0;
		} else {
			return value.intValue();
		}
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Transient
	public boolean isStatsAreStale() {
		return statsAreStale;
	}

	public void setStatsAreStale(boolean statsAreStale) {
		this.statsAreStale = statsAreStale;
	}

	public Map<String, Integer> getSkills() {
		return skills;
	}

	public void setSkills(Map<String, Integer> skills) {
		this.skills = skills;
	}

	public Set<String> getSpells() {
		return spells;
	}

	public void setSpells(Set<String> spells) {
		this.spells = spells;
	}

	public Map<String, ConditionStatus> getConditions() {
		return conditions;
	}

	public void setConditions(Map<String, ConditionStatus> conditions) {
		this.conditions = conditions;
	}

	public Set<String> getPerks() {
		return perks;
	}

	public void setPerks(Set<String> perks) {
		this.perks = perks;
	}

	@XmlElement(name = "baseStats")
	public Map<String, Double> getBaseStats() {
		return baseStats;
	}

	public void setBaseStats(Map<String, Double> baseStats) {
		this.baseStats = baseStats;
	}

	@XmlTransient
	public Map<String, Double> getEffectiveStats() {
		return effectiveStats;
	}

	public void setEffectiveStats(Map<String, Double> effectiveStats) {
		this.effectiveStats = effectiveStats;
	}

	@XmlTransient
	public Map<String, Double> getDerivates() {
		return derivates;
	}

	public void setDerivates(Map<String, Double> derivates) {
		this.derivates = derivates;
	}

	public void setEquippedItems(Map<String, Item> equippedItems) {
		this.equippedItems = equippedItems;
	}

	public int getGold() {
		return gold;
	}

	public void setGold(int gold) {
		this.gold = gold;
	}

	public int getGender() {
		return gender;
	}

	public void setGender(int gender) {
		this.gender = gender;
	}

	@XmlElement(name = "damageMinimal")
	public Map<String, Double> getDamageMinimals() {
		return damageMinimals;
	}

	public void setDamageMinimals(Map<String, Double> damageMinimals) {
		this.damageMinimals = damageMinimals;
	}

	@XmlElement(name = "damageRange")
	public Map<String, Double> getDamageRanges() {
		return damageRanges;
	}

	public void setDamageRanges(Map<String, Double> damageRanges) {
		this.damageRanges = damageRanges;
	}

	@XmlElement(name = "damageResistance")
	public Map<String, Double> getDamageResistances() {
		return damageResistances;
	}

	public void setDamageResistances(Map<String, Double> damageResistances) {
		this.damageResistances = damageResistances;
	}

	public void increaseBaseStat(String statId, double value) {
		Double initialValue = baseStats.get(statId);
		if (initialValue == null) {
			initialValue = 0d;
		}
		baseStats.put(statId, initialValue + value);
	}

	public boolean isAddThe() {
		return addThe;
	}

	public void setAddThe(boolean addThe) {
		this.addThe = addThe;
	}

	public abstract boolean isEnemy();

	// helper methods
	public double getHealth() {
		Double health = baseStats.get(StatConstants.HEALTH);
		return health == null ? 0 : health;
	}

	public void setHealth(double health) {
		baseStats.put(StatConstants.HEALTH, health);
	}

	public double getMaxHealth() {
		Double maxHealth = derivates.get(StatConstants.D_MAX_HEALTH);
		return maxHealth == null ? 0 : maxHealth;
	}

	public double getStamina() {
		Double health = baseStats.get(StatConstants.STAMINA);
		return health == null ? 0 : health;
	}

	public void setStamina(double health) {
		baseStats.put(StatConstants.STAMINA, health);
	}

	public double getMaxStamina() {
		Double maxHealth = derivates.get(StatConstants.D_MAX_STAMINA);
		return maxHealth == null ? 0 : maxHealth;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public int getExperience() {
		return experience;
	}

	public void setExperience(int experience) {
		this.experience = experience;
	}

}
